package controllers;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import models.admin.User;
import play.db.jpa.JPA;
import play.mvc.Controller;
import play.mvc.With;
import utils.geko.crud.CRUD;
import utils.geko.crud.CRUDField;
import utils.geko.crud.CRUDFieldRecord;
import controllers.Secure.Security;

/**
 * @author rjanixz
 *
 */
@With(Secure.class)
public abstract class BaseController extends Controller {
	
	private static final int DEFAULT_PAGE_SIZE = 10;
	private static final int[] PAGE_SIZE_LIST = {5,10,20,50,100};
	
	@Check(value = "VIEW")
	public static void all() throws ClassNotFoundException {
		String defaultSortName = "";
		String defaultSortOrder = "";
		final int defaultPageSize = DEFAULT_PAGE_SIZE;
		final int[] pageSizeList = PAGE_SIZE_LIST;
		final String listURL = getPath("list");
		final List<CRUDFieldRecord> fields = new ArrayList();
		
		boolean isSearchable = false;
		Class entity = getModelClass();
		if(entity.isAnnotationPresent(CRUD.class)) {
			Annotation annotation = entity.getAnnotation(CRUD.class);
			CRUD crud = (CRUD)annotation;
			
			defaultSortName = crud.defaultSortName();
			defaultSortOrder = crud.defaultSortOrder().toString().toLowerCase();
			isSearchable = crud.isSearchable();
			
			for(Field field : entity.getDeclaredFields()) {
				if(field.isAnnotationPresent(CRUDField.class)) {
					Annotation fieldAnnotation = field.getAnnotation(CRUDField.class);
					CRUDField crudField = (CRUDField)fieldAnnotation;
					
					CRUDFieldRecord record = new CRUDFieldRecord(getModule(),getController(),"all");
					record.setName(field.getName());
					record.setSortable(crudField.sortable());
					fields.add(record);
				}
			}
			
		}
		renderTemplate(getPath("all.html"),defaultSortName,defaultSortOrder,isSearchable,defaultPageSize,pageSizeList,listURL,fields);
	}
	
	/**
	 * @param sort Entity field name to be sorted
	 * @param order The <code>asc</code> or <code>desc</code> order for the sort
	 * @param limit page size
	 * @param offset first record 
	 * @throws ClassNotFoundException 
	 * */
	@Check(value="VIEW")
	public static void list(String search, List<String> searchFields, String sort, String order, int limit, int offset) throws ClassNotFoundException {

		request.format = "json";
		
		final String model = getModel();
		
		StringBuilder query = new StringBuilder();
		query.append("From ").append(model).append(" m");
		
		if (search != null && !"".equals(search)) {
			
			Class entity = getModelClass();
			
			boolean filter = false;
			for(Field field : entity.getDeclaredFields()) {
				if(field.isAnnotationPresent(CRUDField.class)) {
					
					Annotation annotation = field.getAnnotation(CRUDField.class);
					CRUDField crudField = (CRUDField)annotation;

					if (crudField.searchable()) {
						if (filter) {
							query.append(" OR ");
						} else{
							query.append(" WHERE ");
							filter=true;
						}
						query.append("m.").append(field.getName()).append(" like '%").append(search).append("%'");
					}
				}
			}
		}
		
		final int total = JPA.em().createQuery(query.toString()).getResultList().size();
	
		//Sorting configuration
		if (sort != null && !"".equals(sort)) {
			query.append(" ORDER BY m.").append(sort).append(" ").append(order);
		}
		final List list = JPA.em().createQuery(query.toString()).setFirstResult(offset).setMaxResults(limit).getResultList();
		
		renderTemplate(getPath("list.json"),list,total);
	}
	
	@Check(value = "DELETE")
	public static void delete(Long  id) {
		final String model = getModel();
		JPA.em().createQuery(" delete from " +model +" m where m.id ="+id).executeUpdate();
		redirect(getPath("all"));
	}
	
	@Check(value = "ADD")
	public static void add() {
		renderTemplate(getPath("add.html"));
	}
	
	@Check(value = "EDIT")
	public static void edit(Long id) {
		final String model = getModel();
		Object entity = JPA.em().createQuery("from " +model +" m where m.id ="+id).getSingleResult();

		renderTemplate(getPath("edit.html"),entity);
	}
	
	//TODO check concurrency and apply cache to this to avoid a lot of queries
	public static User getLoggedUser() {
		return User.find("byEmail", Security.connected()).first();
	}
	
	//TODO check concurrency and apply cache to this to avoid a lot of queries
	public static String connected() {
		return Security.connected();
	}
	
	private static String getModel() {
		final String controller = params.get("controller");
		return controller.substring(0, 1).toUpperCase() + controller.substring(1,controller.length()-1);
	}
	
	private static String getModule() {
		return params.get("module");
	}
	
	private static String getController() {
		return params.get("controller");
	}
	
	private static Class getModelClass() throws ClassNotFoundException{
		return Class.forName("models."+getModule()+"."+getModel());	
	}
	
	private static String getPath(String method) {
		return "/"+getModule()+"/"+getController()+"/"+method;
	}
}